<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        // // 一,call
        // function person(a, b, c) {
        //     console.log(this);
        //     console.log(this.name, this.age)
        // }
        // person();  // 1.直接调用，this指向window
        // // 打印  window
        // // 打印  undefined,undefined
        // const obj = { name: "owl", age: 18 }
        // person.call(obj)                 // 2.传入指定的this值
        // // 打印 {name: 'owl', age: 18}
        // // owl 18
        // function person(a, b, c) {
        //     console.log(a, b, c)
        //     console.log(this);
        //     console.log(this.name, this.age)
        // }
        // person.call(obj, 1, 2, 3)          // 3.给person函数传入指定的this值和实参值
        // // 打印 1 2 3
        // // {name: 'owl', age: 18}
        // // owl 18
        // // 2. 实现myCall方法
        // // 介绍完call()方法以后，我们来尝试写一个自己的myCall方法，具体实现代码和注释如下：
        // Function.prototype.myCall = function (obj) {
        //     if (typeof this !== "function") {
        //         throw new Error(
        //             "Function.prototype.myCall - what is trying to be bound is not callable"
        //         );
        //     }
        //     const ctx = obj || window; // 1.定义一个ctx变量获取传入的对象obj，如果没有则取window
        //     ctx.func = this;           // 2.在ctx对象上新增一个属性func，并且给他赋值为this
        //     // this就是调用myCall函数的函数,在本例中就是person()方法
        //     const args = Array.from(arguments).slice(1); // 3.处理传入的参数，第一个参数为对象obj，
        //     // 所以从第二个开始参数截取
        //     const result = arguments.length > 1 ? ctx.func(...args) : ctx.func();
        //     // 4. 如果传入参数，我们就把实参带入到func函数中执行，如果没有，则直接执行。
        //     delete ctx.func;   // 5. 执行完函数以后，记得删除掉这个“中间变量”属性 ctx
        //     return result;     // 6. 返回result
        // };
        // var dbj = {
        //     name: "owllai",
        // };
        // function testCall(a, b, c) {
        //     console.log(54, this.name, a, b, c);
        // }
        // testCall.myCall(dbj, 1, 2, 3)





        // // 二、apply
        // // apply和call的唯一区别就在于，接收的第二个参数为类数组。
        // // 除此之外，和call几乎一模一样，所以我们在使用和实现自定义apply方法的代码里只需要修改对应的部分就行了。
        // // 1.使用方法
        // function person(a, b, c) {
        //     console.log(this);
        //     console.log(this.name, this.age)
        // }
        // person();  // 1.直接调用，this指向window
        // // 打印  window
        // // 打印  undefined,undefined
        // const obj = { name: "owl", age: 18 }
        // person.apply(obj)                 // 2.传入指定的this值
        // // 打印 {name: 'owl', age: 18}
        // // owl 18
        // function person(a, b, c) {
        //     console.log(a, b, c)
        //     console.log(this);
        //     console.log(this.name, this.age)
        // }
        // person.apply(obj, [1, 2, 3])       // 3.给person函数传入指定的this值和实参值(类数组对象)
        // // 打印 1 2 3
        // // {name: 'owl', age: 18}
        // // owl 18

        // // 2.实现myApply方法
        // // myApply方法里面，我们只需要更改两点：
        // // 第三步获取参数上，直接获取arguments数组的第二项
        // // 第四步调用方法上，传入获取到的arguments 数组的第二项
        // Function.prototype.myApply = function (obj) {
        //     if (typeof this !== "function") {
        //         throw new Error(
        //             "Function.prototype.myApply - what is trying to be bound is not callable"
        //         );
        //     }
        //     const ctx = obj || window; // 1.定义一个ctx变量获取传入的对象obj，如果没有则取window
        //     ctx.func = this;           // 2.在ctx对象上新增一个属性func，并且给他赋值为this
        //     // this就是调用myApply函数的函数,在本例中就是person()方法
        //     const args = arguments[1]; // 3.处理传入的参数，第一个参数为对象obj，
        //     // 第二个参数为数组实参
        //     const result = arguments[1] ? ctx.func(...arguments[1]) : ctx.func();
        //     //第四步： 调用方法，获得结果。
        //     delete ctx.func;
        //     return result;
        // };





        //三, bind
        //使用方法
        // bind接收的参数形式和call有点相似的：第一个参数是指定this指向的值，第二个参数开始则是执行函数需要的形参
        // bind方法在调用以后不会像call、apply一样直接执行，而是返回一个新函数。
        // bind(thisArg)
        // bind(thisArg, arg1)
        // bind(thisArg, arg1, arg2)



        function person(a, b, c) {
            console.log(this);
            console.log(this.name, this.age);
        }
        const obj = { name: "owl", age: 18 };
        let newPerson = person.bind(obj);
        console.log(newPerson); // ƒ person(a, b, c) {
        //      console.log(this);
        //      console.log(this.name, this.age);
        //    }
        newPerson();            // {name: 'owl', age: 18}

        // 2.实现myBind方法
        // 简单版本
        Function.prototype.myBind = function (obj) {
            if (typeof this !== "function") {
                throw new Error(
                    "Function.prototype.bind - what is trying to be bound is not callable"
                );
            }
            var self = this; // 1. 这个this代表调用myBind方法的函数，在本例中也就是person
            var args = Array.prototype.slice.call(arguments, 1);
            // 2. 获取传入的其他参数，从arguments数组的第二项开始截取
            var fn = function () {        // 3.定义一个要返回的函数   
                //4. 返回的新函数也是可以接收参数的，所以我们要一起获取                         
                var newArgs = Array.prototype.slice.call(arguments);
                //5. 将obj和参数一起传入，使用apply来执行
                return self.apply(obj, args.concat(newArgs));
            };
            //6. 最后返回结果函数
            return fn;
        };

        let newBind = person.myBind(obj, 1, 2, 3);
        newBind();


    </script>
</body>

</html>